#include "lcd.h"
#include "time.h"

volatile unsigned char xdata cursor_pos;
volatile unsigned char xdata lcd_map[4][20];

//custom characters live in eeprom
/*
unsigned char  back_slash[8]	= { 0x00,0x10,0x08,0x04,0x02,0x01,0x00,0x00 };
unsigned char code degrees[8] 		= { 0x0C,0x12,0x12,0x0C,0x00,0x00,0x00,0x00 };
*/
										 
unsigned char xdata back_slash[8];
unsigned char xdata degrees[8];

//splash now lives in eeprom
/*											   
unsigned char code splash[81] = {//   0    1    2    3    4    5    6    7    8    9    0    1    2    3    4    5    6    7    8    9
									0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x2F,0x2F,0x20,0x20,0x20,0x2F,0x2F,0x20,0x20,
									0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x00,0x00,0x2F,0x2F,0x20,0x20,0x20,0x2F,0x2F,0x20,0x20,0x20,
									0x20,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x2F,0x2F,0x00,0x00,0x20,0x20,0x2F,0x2F,0x20,0x20,0x20,0x20,
									0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x2F,0x2F,0x20,0x20,0x00,0x00,0x2F,0x2F,0x20,0x20,0x20,0x20,0x20,
								 };
*/
/*
The splash above looks like this on the LCD:
+-----------------------------+
|   \ \    \ \   / /    / /   |
|    \ \    \ \ / /    / /    |
|     \ \   / / \ \   / /     |
|      \ \ / /   \ \ / /      |
+-----------------------------+
*/

unsigned char splash[80];

//draw a splash screen!
void lcd_splash(){
	unsigned char xdata x, my_sec = seconds + 3;
	
	lcd_clear();
	for(x = 0; x < sizeof splash; x++)
		lcd_write_character(splash[x]);
	
	while(my_sec > seconds)	
		sleep();
	
	lcd_clear();
}

//clock in the 4-bit character at the current cursor position
void byte_clock(char command, unsigned short del){
		P1 &= 0xF0;
		P1 |= ((command & 0xF0) >> 4);
		//clock in high nibble
		LCD_E = 1;
		lcd_command_delay(del);
		LCD_E = 0;
		lcd_command_delay(del);
		P1 &= 0xF0;
		P1 |= (command & 0x0F);
		//clock in low nibble
		LCD_E = 1;
		lcd_command_delay(del);
		LCD_E = 0;
		lcd_command_delay(del);
}

//issue a command to the LCD
void lcd_command(unsigned char command){
	
	LCD_RS = 0; //command mode
	byte_clock(command, 150);
}

//clear the display, reset the buffer and buffer pointer
void lcd_clear(){
	cursor_pos = 0x00;
	memset(lcd_map, 0xFF, sizeof(lcd_map));
	lcd_command(0x01);
}

//define a custom character -- a good resource is: http://www.quinapalus.com/hd44780udg.html KUDOS!
void lcd_define_custom_character(unsigned char ch[], unsigned char no){
	unsigned char xdata i = 0;
	if(no > 8)
		return;

	LCD_RS = 0;
	LCD_RW = 0; 

	lcd_command(0x40 + (no << 3));

	LCD_RS = 1;

	for(i = 0; i < 8; i++){
		byte_clock(ch[i], 150);
	}
	LCD_RS = 0;
	P1 = 0x80;
	LCD_E = 1;
	lcd_command_delay(150);
	LCD_E = 0;

	lcd_clear();
	
}

void lcd_initialize()
{
   //command mode
   LCD_E = 0;
   LCD_RW = 0;
   LCD_RS = 0;
   
   lcd_command(0x28);  //2 line 5x7
   lcd_command(0x08);  //turn LCD OFF
   lcd_command(0x0C);  //turn display on, cursor, blinking
   lcd_command(0x01);  //clear display
     
   lcd_define_custom_character(degrees, 1);
   lcd_define_custom_character(back_slash, 0);
   lcd_splash();
   lcd_clear();
}

//writing characters and commands require different timeouts
void lcd_command_delay(unsigned short del){
	unsigned short xdata x = 0;
 	while(x++ < del);
	return;
}

/*
	4x20 DRAM ADDRESSES
	00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13	
	40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53
	14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27
	54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67
*/

//write len characters at row, col on the display -- the cursor indices are shown above
//which we need to keep track of
void lcd_write_string_at_pos(unsigned char ch[], unsigned char row, unsigned char col, unsigned char len){
	unsigned char xdata max_pos = 0x00, i = 0;
	switch(row){
		case 1:
			max_pos = 0x13;
			if(0x00 + col > max_pos)
				return;
			cursor_pos = 0x00 + col;
		break;
		case 2:
			max_pos = 0x53;
			if(0x40 + col > max_pos)
				return;
			cursor_pos = 0x40 + col;
		break;
		case 3:
			max_pos = 0x27;
			if(0x14 + col > max_pos)
				return;
			cursor_pos = 0x14 + col;
		break;
		case 4:
			max_pos = 0x67;
			if(0x54 + col > max_pos)
				return;
			cursor_pos = 0x54 + col;
		break;
		default:
			return;
		break;
	}
	lcd_command(0x80 | cursor_pos);
	while(i < len && cursor_pos <= max_pos)
		lcd_write_character(ch[i++]); 
	
}

//check which character is at row, col
unsigned char get_curr_lcd_char(){
		//row 1
	if(cursor_pos <= 0x13){
		return lcd_map[0][cursor_pos];
	}
	//row 2
	else if(cursor_pos >= 0x40 && cursor_pos <= 0x53){
		return lcd_map[1][(cursor_pos - 0x40)];
	}
	//row 3
	else if(cursor_pos >= 0x14 && cursor_pos <= 0x27){
		return lcd_map[2][(cursor_pos - 0x14)];
	}
	//row 4
	else if(cursor_pos >= 0x54 && cursor_pos <= 0x67){
		return lcd_map[3][(cursor_pos - 0x54)];
	}
}

//update our buffer
void update_lcd_map(unsigned char ch, unsigned char pos){

	//row 1
	if(pos <= 0x13){
		lcd_map[0][pos] = ch;
	}
	//row 2
	else if(pos >= 0x40 && pos <= 0x53){
		lcd_map[1][(pos - 0x40)] = ch;
	}
	//row 3
	else if(pos >= 0x14 && pos <= 0x27){
		lcd_map[2][(pos - 0x14)] = ch;
	}
	//row 4
	else if(pos >= 0x54 && pos <= 0x67){
		lcd_map[3][(pos - 0x54)] = ch;
	}

}

/*
void lcd_write_string(unsigned char ch[]){
	 unsigned char xdata i = 0;
	 while(ch[i] != '\0'){
	 	lcd_write_character(ch[i]);
	 	i++;
	 } 
}
*/
/*
void lcd_write_string_s(unsigned char ch[], unsigned char len){
	 unsigned char xdata i = 0;
	 while(i < len){
	 	lcd_write_character(ch[i]);
	 	i++;
	 } 
}
*/

//write a single character at the current cursor position
void lcd_write_character(unsigned char ch) 
{	

   	//super cheater double buffer!
	if(get_curr_lcd_char() != ch){
		LCD_RW = 0;
		LCD_E = 0;
		LCD_RS = 1; //data mode
		byte_clock(ch, 5);
		/*
		P1 &= 0xF0;
		P1 |= ((ch & 0xF0) >> 4);
		//clock in high nibble
		LCD_E = 1;
		lcd_delay();
		LCD_E = 0;
		
		P1 &= 0xF0;
		P1 |= (ch & 0x0F);
		//clock in low nibble
		LCD_E = 1;
		lcd_delay();
		LCD_E = 0;
		lcd_delay();
		*/
		LCD_RS = 0; //back to command mode
		
		update_lcd_map(ch, cursor_pos);
	}
	else
		lcd_command(0x80 | cursor_pos + 1);	
	
	//update the cursor position
	cursor_pos++;
	
	if(cursor_pos == 0x14){ //next pos 0x40
		cursor_pos = 0x40;
		lcd_command(0x80 | cursor_pos);		
	}

	else if(cursor_pos == 0x28){ //next pos 0x54
		cursor_pos = 0x54;
		lcd_command(0x80 | cursor_pos);
	}
	
	else if(cursor_pos == 0x54){ //next pos 0x14
		cursor_pos = 0x14;
		lcd_command(0x80 | cursor_pos);
	}
	
	else if(cursor_pos == 0x68){ //next pos 0x00 -- scroll line?
		cursor_pos = 0x00;
		lcd_command(0x80 | cursor_pos);
	}    
}
